package script.grammar.expression;


import java.util.HashMap;
import java.util.Map;

import script.grammar.LocScriptLexer;
import script.grammar.symbol.NativeType;
import script.grammar.symbol.SymbolTable;
import script.grammar.symbol.Type;



public class Expression {
	private SymbolTable symbolTable;
	private final Map<Integer,Operator> operators;
	
	public Expression(SymbolTable symbolTable) {
		this.symbolTable = symbolTable;
		this.operators = new HashMap<Integer,Operator>();
		initOps();
	}
	
	public SymbolTable getSymbolTable() {
		return symbolTable;
	}
	
	public Operator getOperatorFromIndex(int index) {
		return operators.get(index);
	}
	
	public Type computeType(int i, Type... operands) {
		Operator op = operators.get(i);
		if (op == null) {
			return NativeType.VOID;
		} else {
			return op.computeType(operands);
		}
	}
	
	private void initOps() {
		operators.put(LocScriptLexer.PLUS, new ArithmeticOperator("+"));
		operators.put(LocScriptLexer.MINUS, new ArithmeticOperator("-"));
		operators.put(LocScriptLexer.MULT, new ArithmeticOperator("*"));
		operators.put(LocScriptLexer.DIV, new ArithmeticOperator("/"));
		operators.put(LocScriptLexer.MOD, new ArithmeticOperator("%"));
		
		operators.put(LocScriptLexer.GREATER_THAN, new RelationalOperator(">"));
		operators.put(LocScriptLexer.LESS_THAN, new RelationalOperator("<"));
		operators.put(LocScriptLexer.GREATER_THAN_EQUALS, new RelationalOperator(">="));
		operators.put(LocScriptLexer.LESS_THAN_EQUALS, new RelationalOperator("<="));
		
		operators.put(LocScriptLexer.EQUALS, new EqualityOperator("=="));
		operators.put(LocScriptLexer.NOT_EQUALS, new EqualityOperator("!="));
		
		operators.put(LocScriptLexer.AND, new BooleanOperator("&&"));
		operators.put(LocScriptLexer.OR, new BooleanOperator("||"));
		
		operators.put(LocScriptLexer.BIT_AND, new BitwiseOperator("&"));
		operators.put(LocScriptLexer.BIT_OR, new BitwiseOperator("|"));
		operators.put(LocScriptLexer.XOR, new BitwiseOperator("^"));
		
		operators.put(LocScriptLexer.SHIFT_LEFT, new ShiftOperator("<<"));
		operators.put(LocScriptLexer.SHIFT_RIGHT, new ShiftOperator(">>"));
		operators.put(LocScriptLexer.U_SHIFT_RIGHT, new ShiftOperator(">>>"));
		
		operators.put(LocScriptLexer.ASSIGN, new AssignmentOperator("="));
		operators.put(LocScriptLexer.PLUS_ASSIGN, new AssignmentOperator("+="));
		operators.put(LocScriptLexer.MINUS_ASSIGN, new AssignmentOperator("-="));
		operators.put(LocScriptLexer.MULT_ASSIGN, new AssignmentOperator("*="));
		operators.put(LocScriptLexer.DIV_ASSIGN, new AssignmentOperator("/="));
		
		operators.put(LocScriptLexer.COND_OP, new TernaryOperator() {
			@Override
			public String getName() {
				return "?:";
			}

			@Override
			protected Type computeTypeImpl(Type o1, Type o2, Type o3) {
				if (!o1.equals(NativeType.BOOLEAN)) {
					return NativeType.VOID;
				} else if (!o2.equals(o3)) {
					if (o2.equals(NativeType.INT) && o3.equals(NativeType.DOUBLE)
							|| o3.equals(NativeType.INT) && o2.equals(NativeType.DOUBLE)) {
						return NativeType.DOUBLE;
					} else {
						return NativeType.VOID;
					}
				} else {
					return o2;
				}
			}
		});
		
		operators.put(LocScriptLexer.NOT, new UnaryOperator() {
			@Override
			public String getName() {
				return "!";
			}

			@Override
			protected Type computeTypeImpl(Type operand) {
				return operand.equals(NativeType.BOOLEAN) ? NativeType.BOOLEAN : NativeType.VOID;
			}
		});
		
		operators.put(LocScriptLexer.NEG, new UnaryOperator() {
			@Override
			public String getName() {
				return "-";
			}

			@Override
			protected Type computeTypeImpl(Type operand) {
				if (operand.equals(NativeType.INT) || operand.equals(NativeType.DOUBLE)) {
					return operand;
				} else {
					return NativeType.VOID;
				}
			}
		});
		
		operators.put(LocScriptLexer.BIT_NEG, new UnaryOperator() {
			@Override
			public String getName() {
				return "~";
			}

			@Override
			protected Type computeTypeImpl(Type operand) {
				return operand.equals(NativeType.INT) ? NativeType.INT : NativeType.VOID;
			}
		});
		
		operators.put(LocScriptLexer.PRE_INC, new IncDecOperator("++_"));
		operators.put(LocScriptLexer.PRE_DEC, new IncDecOperator("--_"));
		operators.put(LocScriptLexer.POST_INC, new IncDecOperator("_++"));
		operators.put(LocScriptLexer.POST_DEC, new IncDecOperator("_--"));
	}
}
